package com.example.palette.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class AreaSelectView extends View {

    private static final int TOP = 1;
    private static final int BOTTOM = 2;
    private static final int LEFT = 3;
    private static final int RIGHT = 4;
    private Paint mPaint = new Paint();
    private Paint mPointPaint = new Paint();
    private int mBgColor = Color.parseColor("#40000000");
    private float mLastDownX;
    private float mLastDownY;
    private RectF mRectF = new RectF();
    private RectF mTopRectF = new RectF();
    private RectF mBottomRectF = new RectF();
    private RectF mLeftRectF = new RectF();
    private RectF mRightRectF = new RectF();
    private float mRadius = 30f;
    private boolean mIsDragRect;
    private boolean mIsDragPoint;
    private boolean mIsMoveRect;
    private int mCurrentDirection = -1;
    private ActionUpListener mActionUpListener;

    public AreaSelectView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.TRANSPARENT);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setXfermode(xfermode);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setColor(Color.GREEN);
        mPointPaint.setStyle(Paint.Style.FILL);
        mPointPaint.setXfermode(xfermode);
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        setLayerType(LAYER_TYPE_HARDWARE, null);
        canvas.drawColor(mBgColor);
        if (mIsDragPoint || mIsMoveRect || mIsDragRect) {
            if (!mRectF.isEmpty()) {
                canvas.drawRect(mRectF, mPaint);
                drawFourPoint(canvas);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastDownX = event.getX();
                mLastDownY = event.getY();
                if (isDragPoint(mLastDownX, mLastDownY)) {
                    mIsDragRect = false;
                    mIsDragPoint = true;
                    mIsMoveRect = false;
                } else if (mRectF.contains(mLastDownX, mLastDownY)) {
                    mIsDragRect = false;
                    mIsDragPoint = false;
                    mIsMoveRect = true;
                    mCurrentDirection = -1;
                } else {
                    mIsDragRect = false;
                    mIsDragPoint = false;
                    mIsMoveRect = false;
                    mCurrentDirection = -1;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsDragPoint) {
                    dealMovePoint(event.getX(), event.getY());
                } else if (mIsMoveRect) {
                    dealMoveRect(event.getX(), event.getY());
                } else {
                    mIsDragRect = true;
                    dealDragRect(event.getX(), event.getY());
                }
                break;
            case MotionEvent.ACTION_UP:
                if(mActionUpListener != null && !mRectF.isEmpty()){
                    mActionUpListener.onActionUp(mRectF);
                }
                break;
        }
        return true;
    }

    private void drawFourPoint(Canvas canvas) {
        mTopRectF.set(mRectF.left, mRectF.top - mRadius, mRectF.right, mRectF.top + mRadius);
        mBottomRectF.set(mRectF.left, mRectF.bottom - mRadius, mRectF.right, mRectF.bottom + mRadius);
        mLeftRectF.set(mRectF.left - mRadius, mRectF.top, mRectF.left + mRadius, mRectF.bottom);
        mRightRectF.set(mRectF.right - mRadius, mRectF.top, mRectF.right + mRadius, mRectF.bottom);
        canvas.drawCircle(mRectF.centerX(), mRectF.top, mRadius, mPointPaint);
        canvas.drawCircle(mRectF.centerX(), mRectF.bottom, mRadius, mPointPaint);
        canvas.drawCircle(mRectF.left, mRectF.centerY(), mRadius, mPointPaint);
        canvas.drawCircle(mRectF.right, mRectF.centerY(), mRadius, mPointPaint);
    }

    private boolean isDragPoint(float x, float y) {
        if (mTopRectF.contains(x, y)) {
            mCurrentDirection = TOP;
            return true;
        }
        if (mBottomRectF.contains(x, y)) {
            mCurrentDirection = BOTTOM;
            return true;
        }
        if (mLeftRectF.contains(x, y)) {
            mCurrentDirection = LEFT;
            return true;
        }
        if (mRightRectF.contains(x, y)) {
            mCurrentDirection = RIGHT;
            return true;
        }
        return false;
    }

    private void dealMovePoint(float x, float y) {
        float dx = x - mLastDownX;
        float dy = y - mLastDownY;
        switch (mCurrentDirection) {
            case TOP:
                mRectF.top += dy;
                break;
            case BOTTOM:
                mRectF.bottom += dy;
                break;
            case LEFT:
                mRectF.left += dx;
                break;
            case RIGHT:
                mRectF.right += dx;
                break;
        }
        mLastDownX = x;
        mLastDownY = y;
        invalidate();
    }

    private void dealMoveRect(float x, float y) {
        float dx = x - mLastDownX;
        float dy = y - mLastDownY;
        mRectF.top += dy;
        mRectF.bottom += dy;
        mRectF.left += dx;
        mRectF.right += dx;
        mLastDownX = x;
        mLastDownY = y;
        invalidate();
    }

    private void dealDragRect(float x, float y) {
        float dx = x - mLastDownX;
        float dy = y - mLastDownY;
        if (dx > 0 && dy > 0) {
            mRectF.set(mLastDownX, mLastDownY, 0, 0);
            mRectF.right = x;
            mRectF.bottom = y;
        } else if (dx > 0 && dy < 0) {
            mRectF.set(mLastDownX, 0, 0, mLastDownY);
            mRectF.right = x;
            mRectF.top = y;
        } else if (dx < 0 && dy > 0) {
            mRectF.set(0, mLastDownY, mLastDownX, 0);
            mRectF.left = x;
            mRectF.bottom = y;
        } else if (dx < 0 && dy < 0) {
            mRectF.set(0, 0, mLastDownX, mLastDownY);
            mRectF.left = x;
            mRectF.top = y;
        }
        invalidate();
    }

    public void clear(){
        mRectF.setEmpty();
    }

    public void setActionUpListener(ActionUpListener listener) {
        mActionUpListener = listener;
    }

    public interface ActionUpListener{
        void onActionUp(RectF rectF);
    }
}
